﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

using Storm.TextEditor.Drawing;
using Storm.TextEditor.Win32;
using Storm.TextEditor.Win32.Enums;
using Storm.TextEditor.Win32.Structs;

namespace Storm.TextEditor.Core
{
	[ToolboxItem(false)]
	public class Widget
		: Control
	{
		#region Fields

		private const int WS_EX_CLIENTEDGE = unchecked((int)0x00000200);
		private const int WS_BORDER        = unchecked((int)0x00800000);

		private ControlBorderStyle borderStyle = ControlBorderStyle.FixedSingle;
		private Color              borderColor = Color.Black;

		private Container components = null;
		private bool      runOnce    = true;

		#region Events

		/// <summary>
		/// Occurs when the Widget has been loaded.
		/// </summary>
		public event EventHandler Load = null;

		#endregion

		#endregion

		#region Properties

		/// <summary>
		/// Override CreateParams to tell the OS how we want the Widget to act.
		/// </summary>
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
		[Description("Override CreateParams to tell the OS how we want the Widget to act.")]
		protected override CreateParams CreateParams
		{
			get
			{
				CreateParams cp = base.CreateParams;

				if (BorderStyle == ControlBorderStyle.None)
					return cp;

				cp.ExStyle &= (~WS_EX_CLIENTEDGE);
				cp.Style &= (~WS_BORDER);

				return cp;
			}
		}

		/// <summary>
		/// Gets or sets the ControlBorderStyle of the Widget.
		/// </summary>
		[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
		[Category("Appearance"), Description("Gets or sets the ControlBorderStyle of the Widget.")]
		[DefaultValue(ControlBorderStyle.None)]
		public ControlBorderStyle BorderStyle
		{
			get { return borderStyle; }
			set
			{
				if (borderStyle != value)
				{
					if (Enum.IsDefined(typeof(ControlBorderStyle), value) == false)
						throw new InvalidEnumArgumentException("value", (int)value, typeof(ControlBorderStyle));

					borderStyle = value;
					this.UpdateStyles();
					this.Refresh();
				}
			}
		}

		/// <summary>
		/// Gets or sets the background image of the Widget.
		/// </summary>
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Obsolete("Do not use!", true)]
		[Description("Gets or sets the background image of the Widget.")]
		public new Image BackgroundImage
		{
			get { return base.BackgroundImage; }
			set { base.BackgroundImage = value; }
		}

		/// <summary>
		/// Gets the width of the Widget client excluding borders.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the width of the Widget client excluding borders.")]
		public int ClientWidth
		{
			get { return this.WindowSize.Width - (this.BorderWidth * 2); }
		}

		/// <summary>
		/// Gets the height of the Widget client excluding borders.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the height of the Widget client excluding borders.")]
		public int ClientHeight
		{
			get { return WindowSize.Height - (BorderWidth * 2); }
		}

		/// <summary>
		/// Gets the width of a border depending on the ControlBorderStyle.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the width of a border depending on the ControlBorderStyle.")]
		public int BorderWidth
		{
			get
			{
				switch (borderStyle)
				{
					case ControlBorderStyle.None:
						return 0;
					case ControlBorderStyle.Sunken:
						return 2;
					case ControlBorderStyle.SunkenThin:
						return 1;
					case ControlBorderStyle.Raised:
						return 2;
					case ControlBorderStyle.Etched:
						return 2;
					case ControlBorderStyle.Bump:
						return 6;
					case ControlBorderStyle.FixedSingle:
						return 1;
					case ControlBorderStyle.FixedDouble:
						return 2;
					case ControlBorderStyle.RaisedThin:
						return 1;
					case ControlBorderStyle.Dotted:
						return 1;
					case ControlBorderStyle.Dashed:
						return 1;
				}

				return -1;
			}
		}

		/// <summary>
		/// Gets the size of the Widget.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the size of the Widget.")]
		public Size WindowSize
		{
			get
			{
				RECTAPI s = new RECTAPI();
				NativeUser32Api.GetWindowRect(this.Handle, ref s);

				return new Size(s.Width, s.Height);
			}
		}

		/// <summary>
		/// Gets or sets the color of the border.
		/// </summary>
		[Category("Appearance"), Description("Gets or sets the color of the border.")]
		[DefaultValue(typeof(Color), "Black")]
		public Color BorderColor
		{
			get { return borderColor; }
			set
			{
				borderColor = value;

				this.UpdateStyles();
				this.Refresh();
			}
		}

		#endregion

		#region Methods

		#region Protected

		/// <summary>
		/// Raises the Load event.
		/// </summary>
		/// <param name="e">EventArgs.</param>
		protected virtual void OnLoad(EventArgs e)
		{
			if (this.Load != null)
				this.Load(this, e);

			this.Refresh();
		}

		/// <summary>
		/// Processes Windows messages from the OS.
		/// </summary>
		/// <param name="m">Windows message to process.</param>
		protected override unsafe void WndProc(ref Message m)
		{
			if (m.Msg == (int)WindowMessage.WM_NCPAINT)
				this.RenderBorder();
			else if (m.Msg == (int)WindowMessage.WM_SHOWWINDOW)
			{
				if (runOnce == true)
				{
					runOnce = false;
					this.OnLoad(null);

					this.UpdateStyles();
				}
				else
					this.UpdateStyles();
			}
			else if (m.Msg == (int)WindowMessage.WM_NCCALCSIZE)
			{
				if (m.WParam == (IntPtr)0)
				{
					RECTAPI* pRC = (RECTAPI*)m.LParam;
				}
				else if (m.WParam == (IntPtr)1)
				{
					_NCCALCSIZE_PARAMS* pNCP = (_NCCALCSIZE_PARAMS*)m.LParam;

					int t = pNCP->NewRect.Top + BorderWidth;
					int l = pNCP->NewRect.Left + BorderWidth;
					int b = pNCP->NewRect.Bottom - BorderWidth;
					int r = pNCP->NewRect.Right - BorderWidth;

					pNCP->NewRect.Top = t;
					pNCP->NewRect.Left = l;
					pNCP->NewRect.Right = r;
					pNCP->NewRect.Bottom = b;
				}
			}

			base.WndProc(ref m);
		}


		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (components != null)
				{
					components.Dispose();
				}
			}

			base.Dispose(disposing);
		}

		#endregion

		#region Private

		/// <summary>
		/// Draws the border of the Widget.
		/// </summary>
		private void RenderBorder()
		{
			IntPtr hdc = NativeUser32Api.GetWindowDC(Handle);
			RECTAPI s = new RECTAPI();
			NativeUser32Api.GetWindowRect(Handle, ref s);

			using (Graphics g = Graphics.FromHdc(hdc))
			{
				DrawingTools.DrawBorder((ControlBorderStyle)(int)BorderStyle, BorderColor, g, new Rectangle(0, 0, s.Width, s.Height));
			}

			NativeUser32Api.ReleaseDC(Handle, hdc);
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of Widget.
		/// </summary>
		public Widget()
		{
			this.InitializeComponent();
		}

		#region Component Designer generated code

		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			// 
			// BaseControl
			// 
			Size = new System.Drawing.Size(272, 264);
		}

		#endregion
	}
}
